#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

#define SIZE 128
int argc;
char *argv[SIZE];
char pwd[SIZE];
char pwdenv[SIZE];
char *genv[SIZE];
int lastcode = 0;

string GetUsername()
{
    return getenv("USER");
}

string GetHostname()
{
    return getenv("HOSTNAME");
}

string GetPWD()
{
    getcwd(pwd, sizeof(pwd));
    sprintf(pwdenv, "PWD=%s", pwd);
    putenv(pwdenv);
    return pwd;

}

string MakeComLin()
{
    char line[SIZE] = {0};
    string tmp = GetPWD();
    if(tmp != "/")
    {
      size_t pos = tmp.rfind("/");
      tmp = tmp.substr(pos + 1);
    }
    snprintf(line, SIZE, "[%s@%s %s]%% ", GetUsername().c_str(), GetHostname().c_str(), tmp.c_str());
    return line;
}

void PrintComLin()
{
    printf("%s", MakeComLin().c_str());  
    fflush(stdout);
}

bool GetComLin(char arr[], int size)
{
    char *result = fgets(arr, size, stdin);
    if(!result)
      return false;
    arr[strlen(arr) - 1] = '\0';
    if(strlen(arr) == 0) return false;
    return true;
}

bool ProcessComLin(char arr[])
{
    const char *s = " ";
    memset(argv, 0, sizeof(argv));
    argc = 1;


    argv[argc] = strtok(arr, s);

    while((argv[++argc] = strtok(nullptr, s)) != 0);
    return true; 
}

void debug()
{
  printf("argc:%d\n", argc);
  for(int i = 0; i < argc; i++)
  {
    printf("argv[%d]:%s\n", i, argv[i]);
  }
}

void AddEnv(const char *item)
{
  int index = 0;
  while(genv[index])
  {
    index++;
  } 
  genv[index] = (char*)malloc(strlen(item)+1);
  strncpy(genv[index], item, strlen(item)+1);
  genv[++index] = nullptr;
}

bool CheckAndExecBuiltCommand()
{
    if(strcmp(argv[0], "cd") == 0)
    {
        // 内建命令
        if(argc == 2)
        {
            chdir(argv[1]);
            lastcode = 0;
        }
        else 
        {
          lastcode = 1;
        }
        return true;
    }
    else if(strcmp(argv[0], "export") == 0)
    {
      if(argc == 2)
      {
        AddEnv(argv[1]);
        lastcode = 0;
      }
      else 
      {
        lastcode = 1;
      }
      return true;
    }
    else if(strcmp(argv[0], "env") == 0)
    {
      for(int i = 0; genv[i]; i++)
      {
        printf("%s\n", genv[i]);
      } 
      return true;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
      if(argc == 2)
      {
        if(argv[1][0] == '$')
        {
          if(argv[1][1] == '?')
          {
            printf("%d\n", lastcode);
            lastcode = 0;
          }
          else 
          {
            //查询变量暂时不写
          }
        }
      }
      else 
      {
        lastcode = 2;
      }
      return true;
    }
    return false;
}

bool ExcuteComLin()
{
  if(!CheckAndExecBuiltCommand())
  {
    pid_t id = fork();
    if(id < 0)  return false;
    else if(id == 0)
    {
      execvpe(argv[0], argv, genv);
      exit(1);
    } 
    int status;
    pid_t pid = waitpid(id, &status, 0);
    if(pid > 0)
    {
        if(WIFEXITED(status))
        {
          lastcode = WIFEXITED(status);
        }
        else 
        {
          lastcode = 2;
        }
        return true;
    }
    return false;
  }
  return true;
}

void EnvInit()
{
  extern char **environ;
  int index = 0;
  while(environ[index])
  {
    genv[index] = (char*)malloc(strlen(environ[index]+1));
    strncpy(genv[index], environ[index], strlen(environ[index]));
    index++;
  }
  genv[index] = nullptr;
}

int main()
{
  char comlin_data[SIZE] = {0};
  EnvInit();
  while(1)
  {
    //命令行提示符打印
    PrintComLin();

    //获取用户命令
    if(!GetComLin(comlin_data, SIZE))
    {
      continue;
    }

    //分析命令
    ProcessComLin(comlin_data);
    //debug();

    //执行命令
    ExcuteComLin();
  }

  return 0;

}
